package com.zr.config;

import com.zr.pojo.Result;
import com.zr.security.MyUserDetailsService;
import com.zr.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import org.springframework.web.cors.CorsUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * Spring Security配置类
 */
@Slf4j
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)//启用Security注解，例如最常用的@PreAuthorize
//@Order  //默认最低优先级
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    protected MyUserDetailsService myUserDetailsService;
    @Autowired
    protected BCryptPasswordEncoder passwordEncoder;


    /**
     * 用户认证配置
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder);
    }

    @Override
    public void configure(WebSecurity web){
        //web.ignoring().antMatchers("/css/**","/js/**","/img/**","/plugins/**");
    }

    /**
     * Http安全配置
     */

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
                .csrf().disable()//禁用CSRF跨域
                .cors() //开启跨域
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                //.anonymous().disable()  //关闭匿名用户
                .and()
                .httpBasic().authenticationEntryPoint(new UnauthorizedEntryPoint()) //定义出错后的异常信息

                .and()
                .formLogin()
                .loginPage("/api/login_page")
                .loginProcessingUrl("/login")                       //设置登陆的请求的路径
                .successHandler(new AjaxAuthSuccessHandler())  //自定义登陆成功返回数据，否则会默认跳转成功页面
                .failureHandler(new AjaxAuthFailHandler())                 //自定义登陆失败返回数据，否则会默认跳转ERROR页面
                //.usernameParameter("username")
                //.passwordParameter("password")

                .and()
                .authorizeRequests()
                .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() //所有的预检查默认通过

                .antMatchers("/api/login_page","/login").permitAll()                            //登陆方法默认通过
                .antMatchers("/swagger**").permitAll()
                //  enable Swagger
                .antMatchers("/swagger-ui.html", "/v2/**", "/public/**", "/webjars/**", "/configuration/ui", "/swagger-resources/**", "/configuration/**").permitAll()
                .anyRequest().authenticated()   //其余请求路径需要权限验证
                .and()

                /*.headers()
                .frameOptions()   //预防iframe跨域问题
                .disable()*/
                /*.and()*/
                .logout()
                .logoutUrl("/logout").permitAll()                                       //自定义退出登陆URL （方法中设置销毁SESSION等逻辑）
                .logoutSuccessHandler(new AjaxLogoutSuccessHandler());  //自定义退出登陆返回数据，否则会默认跳转登陆页面
       //http.addFilterBefore(new MyWebFilter(), UsernamePasswordAuthenticationFilter.class);//将常用的WEB FILTER 在用户密码验证之前执行
        http.exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint());
    }


    //定义登陆成功返回信息
    private class AjaxAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            response.setContentType("application/json;charset=utf-8");
            response.setStatus(200);
            PrintWriter out = response.getWriter();
            //获取上下文登录信息
           // Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            Object principal = authentication.getPrincipal();
            if (principal != null && principal instanceof UserDetails) {
                UserDetails user = (UserDetails) principal;
                log.info("login success:" + user.getUsername());

                //登录信息维护在session中
                request.getSession().setAttribute("userDetail", user);
                //response.getWriter().write(JsonUtils.toString(authentication));
                //UserDTO userDTO = authService.selectDtoByLoginName(user.getUsername());
                //userDTO.setPassword(null);
                //登录成功，返回查询到用户登录信息
                out.write(JsonUtils.toString(Result.success(user)));
                out.flush();
                out.close();
            }
        }
    }

    //覆写commence方法，修改返回状态值为401，修改配置。让前端去获取状态码执行前端的逻辑。
    private class CustomAuthenticationEntryPoint  implements AuthenticationEntryPoint  {
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response,
                             AuthenticationException authException) throws IOException, ServletException {
            response.setContentType("application/json;charset=utf-8");
            response.setStatus(200);
            PrintWriter out = response.getWriter();
            out.write(JsonUtils.toString(Result.authFail("未登录")));
            out.flush();
            out.close();
        }

    }


    //定义登陆失败返回信息
    private class AjaxAuthFailHandler extends SimpleUrlAuthenticationFailureHandler {
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
            response.setContentType("application/json;charset=utf-8");
            response.setStatus(200);
            log.info("login fail");
            PrintWriter out = response.getWriter();
            out.write(JsonUtils.toString(Result.authFail("登录失败")));
            out.flush();
            out.close();
        }
    }

    //定义异常返回信息
    private class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
       @Override
       public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
           response.setContentType("application/json;charset=utf-8");
           response.sendError(403, authException.getMessage());
           PrintWriter out = response.getWriter();
           log.info("权限不足");
           out.write(JsonUtils.toString(Result.authNullFail("权限不足")));
           out.flush();
           out.close();
       }
   }

    //定义登出成功返回信息
    private class AjaxLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {

        @Override
        public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
                                    Authentication authentication) throws IOException, ServletException {
            response.setContentType("application/json;charset=utf-8");
            response.setStatus(200);
            PrintWriter out = response.getWriter();
            log.info("登出成功");
            out.write(JsonUtils.toString(Result.success("登出成功")));
            out.flush();
            out.close();
        }
    }
}